package com.csizg.facemodel.util;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.CvHistogram;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacpp.opencv_face;
import org.bytedeco.javacpp.opencv_imgcodecs;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import static org.bytedeco.javacpp.helper.opencv_imgproc.cvCalcHist;
import static org.bytedeco.javacpp.opencv_core.CV_HIST_ARRAY;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE;
import static org.bytedeco.javacpp.opencv_imgcodecs.cvLoadImage;
import static org.bytedeco.javacpp.opencv_imgproc.CV_COMP_CORREL;
import static org.bytedeco.javacpp.opencv_imgproc.CV_COMP_INTERSECT;
import static org.bytedeco.javacpp.opencv_imgproc.cvCompareHist;
import static org.bytedeco.javacpp.opencv_imgproc.cvNormalizeHist;

/**
 * 人脸识别工具类
 *
 * @author haohao(ronghao3508@gmail.com) on 2018/5/17 15:39
 * @version v1.0
 */
public class FaceUtil {

    //阈值
    private static final String TAG = "FaceUtil";

    /**
     * 特征保存
     *
     * @param context Context
     * @param image Mat
     * @param rect 人脸信息
     * @param fileName 文件名字
     * @return 保存是否成功
     */
    public static boolean saveImage(Context context, Mat image, Rect rect, String fileName) {
        try {
            // 原图置灰
            Mat grayMat = new Mat();
            Imgproc.cvtColor(image, grayMat, Imgproc.COLOR_BGR2GRAY);
            // 把检测到的人脸重新定义大小后保存成文件
            Mat sub = grayMat.submat(rect);
            Mat mat = new Mat();
            Size size = new Size(200, 200);
            Imgproc.resize(sub, mat, size);
            //Imgproc.equalizeHist(mat,mat);
            return Imgcodecs.imwrite(FaceFileUtil.getFilePath(context, fileName), mat);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 删除特征
     *
     * @param context Context
     * @param fileName 特征文件
     * @return 是否删除成功
     */
    public static boolean deleteImage(Context context, String fileName) {
        // 文件名不能为空
        if (TextUtils.isEmpty(fileName)) {
            return false;
        }
        // 文件路径不能为空
        String path = FaceFileUtil.getFilePath(context, fileName);
        if (TextUtils.isEmpty(path)) {
            File file = new File(path);
            return file.exists() && file.delete();
        } else {
            return false;
        }
    }

    /**
     * 提取特征
     *
     * @param context Context
     * @param fileName 文件名
     * @return 特征图片
     */
    public static Bitmap getImage(Context context, String fileName) {
        String filePath = FaceFileUtil.getFilePath(context, fileName);
        if (TextUtils.isEmpty(filePath)) {
            return null;
        } else {
            return BitmapFactory.decodeFile(filePath);
        }
    }

    public static Bitmap fileToImage(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return null;
        } else {
            return BitmapFactory.decodeFile(filePath);
        }
    }

    /**
     * 特征对比
     *
     * @param context Context
     * @param fileName1 人脸特征
     * @param fileName2 人脸特征
     * @return 相似度
     */
    public static double compare(Context context, String fileName1, String fileName2) {
        String pathFile1 = FaceFileUtil.getFilePath(context, fileName1);
        String pathFile2 = FaceFileUtil.getFilePath(context, fileName2);
        return compareImage(fileName1, fileName2);
    }

    /**
     * 根据一张图片找出相似度大于80d图片
     */
    public static CompareResult compareForFiles(Context context, String filePath, int level) {
        CompareResult result = compareForFiles(context, filePath);
        if (result.getNum() * 100 > level) {
            return result;
        } else {
            return null;
        }
    }

    public static CompareResult compareForFiles(Context context, String filePath) {
        File file = new File(FaceFileUtil.getFileTrainParentPath(context));
        if (!file.exists()) return null;
        File[] files = file.listFiles();
        double max = -1d;
        String path = "";
        for (File tmpFile : files) {
            if (tmpFile.isDirectory() || tmpFile.getName().contains("currentface")) {
                continue;
            }
            double result =
                    compareImage(FaceFileUtil.getFilePath(context, filePath), tmpFile.getPath());
            if (result > max) {
                max = result;
                path = FaceFileUtil.getFilePath(context, filePath);
                //path = tmpFile.fileToImage();
            }
        }
        return new CompareResult(path, max / 100.0d, fileToImage(path));
    }

    /**
     * 比较两张图片相似度
     */
    private static double compareImage(String pathFile1, String pathFile2) {
        try {
            IplImage image1 = cvLoadImage(pathFile1, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
            IplImage image2 = cvLoadImage(pathFile2, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
            if (null == image1 || null == image2) {
                return -1;
            }

            int l_bins = 256;
            int hist_size[] = { l_bins };
            float v_ranges[] = { 0, 255 };
            float ranges[][] = { v_ranges };

            IplImage imageArr1[] = { image1 };
            IplImage imageArr2[] = { image2 };
            CvHistogram Histogram1 = CvHistogram.create(1, hist_size, CV_HIST_ARRAY, ranges, 1);
            CvHistogram Histogram2 = CvHistogram.create(1, hist_size, CV_HIST_ARRAY, ranges, 1);
            cvCalcHist(imageArr1, Histogram1, 0, null);
            cvCalcHist(imageArr2, Histogram2, 0, null);
            cvNormalizeHist(Histogram1, 100.0);
            cvNormalizeHist(Histogram2, 100.0);
            // 参考：http://blog.csdn.net/nicebooks/article/details/8175002
            double c1 = cvCompareHist(Histogram1, Histogram2, CV_COMP_CORREL) * 100;
            double c2 = cvCompareHist(Histogram1, Histogram2, CV_COMP_INTERSECT);
            return (c1 + c2) / 2;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    private static void lightness(Mat src, double a, double g, Mat dst) {
        Mat src2 = new Mat(src.rows(), src.cols(), src.type());
        Core.addWeighted(src, a, src2, 1 - a, g, dst);
    }

    /**
     * 保存训练数据
     */
    public static boolean saveTrainImage(Context context, Mat image, Rect rect, String fileName) {
        try {
            // 原图置灰
            Mat grayMat = new Mat();
            Imgproc.cvtColor(image, grayMat, Imgproc.COLOR_BGR2GRAY);
            // 把检测到的人脸重新定义大小后保存成文件
            Mat sub = grayMat.submat(rect);
            Mat mat = new Mat();
            Size size = new Size(200, 200);
            Imgproc.resize(sub, mat, size);

            //Mat mat1 = new Mat();
            //Mat mat2 = new Mat();
            //lightness(mat, 1, 50, mat1);
            //Imgproc.equalizeHist(mat, mat2);
            Imgcodecs.imwrite(FaceFileUtil.getFileTrainPath(context, fileName), mat);
            //Imgcodecs.imwrite(FaceFileUtil.getFileTrainPath(context, "1" + fileName), mat1);
            //Imgcodecs.imwrite(FaceFileUtil.getFileTrainPath(context, "2" + fileName), mat2);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 训练数据
     */
    public static boolean train(Context context, opencv_face.FaceRecognizer recognizer,
            List<String> hasTrainFiles) {

        File file = new File(FaceFileUtil.getFileTrainParentPath(context));
        if (!file.exists()) return false;
        File[] files = file.listFiles();
        if (files.length == 0) {
            return false;
        }

        opencv_core.MatVector images = new opencv_core.MatVector(files.length);
        opencv_core.Mat labels = new opencv_core.Mat(files.length, 1, CvType.CV_32SC1);
        IntBuffer labelsBuf = labels.createBuffer();

        for (int i = 0; i < files.length; i++) {
            File tmpFile = files[i];
            if (hasTrainFiles.contains(tmpFile.getPath())) {
                continue;
            }
            hasTrainFiles.add(tmpFile.getPath());
            images.put(i, getMat(tmpFile.getPath()));
            opencv_core.Mat labelMat = new opencv_core.Mat(i, files.length, opencv_core.CV_32SC1);

            labelsBuf.put(i, Integer.valueOf(tmpFile.getName().substring(0, 1)));
        }

        //recognizer.deallocate();
        //recognizer = opencv_face.createLBPHFaceRecognizer(1, 8, 8, 8, 100);

        recognizer.train(images, labels);

        return true;
    }

    /**
     * 训练数据
     */
    public static boolean update(Context context, opencv_face.FaceRecognizer recognizer,
            List<String> hasTrainFiles) {
        File file = new File(FaceFileUtil.getFileTrainParentPath(context));
        if (!file.exists()) return false;
        File[] files = file.listFiles();
        List<File> tmpFiles = new ArrayList<>();
        if (files.length == 0) {
            return false;
        }
        for (File file1 : files) {
            if (!hasTrainFiles.contains(file1.getPath())) {
                tmpFiles.add(file1);
            }
        }
        if (tmpFiles.size() == 0) {
            return true;
        }

        opencv_core.MatVector images = new opencv_core.MatVector(tmpFiles.size());
        opencv_core.Mat labels = new opencv_core.Mat(tmpFiles.size(), 1, CvType.CV_32SC1);
        IntBuffer labelsBuf = labels.createBuffer();

        for (int i = 0; i < tmpFiles.size(); i++) {
            File tmpFile = files[i];
            hasTrainFiles.add(tmpFiles.get(i).getPath());
            images.put(i, getMat(tmpFiles.get(i).getPath()));
            opencv_core.Mat labelMat = new opencv_core.Mat(i, files.length, opencv_core.CV_32SC1);
            labelsBuf.put(i, Integer.valueOf(tmpFile.getName().substring(0, 1)));
        }
        recognizer.update(images, labels);
        return true;
    }

    /**
     * 识别
     */
    public static CompareResult predict(Context context, opencv_face.FaceRecognizer recognizer,
            String src) {

        File file = new File(FaceFileUtil.getFilePath(context, src));
        try {
            IntPointer intPointer = new IntPointer(1);
            intPointer.put(-1);
            DoublePointer doublePointer = new DoublePointer(1);
            doublePointer.put(0.0d);
            recognizer.predict(getMat(file.getPath()), intPointer, doublePointer);
            Log.e("识别相似度", intPointer.get() + "------" + doublePointer.get());
            return new CompareResult(intPointer + "", doublePointer.get(),
                    fileToImage(file.getAbsolutePath()), intPointer.get());
        } catch (Exception e) {
            return null;
        }
    }

    private static opencv_core.Mat getMat(String path) {
        return opencv_imgcodecs.imread(path, CV_LOAD_IMAGE_GRAYSCALE);
    }
}
